/**
 * The purpose of this program is a simple test on the arbb library by Intel (R), it simply 
 * created two vectors and performs an element wise addition on them and displays the result.
 * It is created based on the tutorial sample files that are provided with the 
 * Intel Arbb (R) documentations on the following website: 
 * http://software.intel.com/sites/products/documentation/arbb/tutorial/index.htm#closure__call.htm
 * Author: Mohamad Noureddine
 * Date: October 10, 2010
 **/

#include <iostream>
#include <cstdlib>
#include <arbb.hpp>

const unsigned int length = 64;

/* function declaration */
void do_main();
void sum_kernel(const arbb::dense<arbb::f32>& x1, const arbb::dense<arbb::f32>& x2, arbb::dense<arbb::f32>& y);
void outputresult(const float*);

int main() {
  try {
    do_main();
  }
  catch (const std::exception& e) {
    std::cerr << "Error: caught unexpected std::exception." << std::endl;
    std::cerr << "\t" << e.what() << std::endl;
    return 1;
  }
  catch (...) {
    std::cerr << "Error: caught unexpected exception" << std::endl;
    std::cerr << "\t" << "<unknown-exception-type>" << std::endl;
    return 1;
  }

  return 0;
}

void do_main() {
  /* import the arbb types */
  using arbb::f32;
  using arbb::dense;

  /* allocate three float arrays to be used for computation 
   * used the ARBB_CPP_ALIGN macro to impose that the arrays are laid out in 
   * memory in order to fully exploit the SIMD operations 
   */
  ARBB_CPP_ALIGN(float a[length]);
  ARBB_CPP_ALIGN(float b[length]);
  ARBB_CPP_ALIGN(float out[length]);

  for (unsigned int i = 0; i < length; i++) {
    a[i] = i;
    b[i] = i*i;
  }

  /* declare arbb vectors */
  dense<f32> vec_a;
  dense<f32> vec_b;
  dense<f32> vec_out;

  /* bind the arbb vectors with the input data */
  bind(vec_a, a, length);
  bind(vec_b, b, length);
  bind(vec_out, out, length);

  std::cout << "a is:" << std::endl;
  outputresult(a);
  std::cout << "b is:" << std::endl;
  outputresult(b);

  std::cout << "a + b is:" << std::endl;
  /* Call the summation kernel on the arbb vectors a and b */
  call(sum_kernel)(vec_a, vec_b, vec_out);
  outputresult(out);

}


void sum_kernel(const arbb::dense<arbb::f32>& x1, const arbb::dense<arbb::f32>& x2, arbb::dense<arbb::f32>& y) {
  /** 
   * Note that here we are simly saying a one line add operation 
   * instead of using the traditional for loop.
   * This is in theory equivalent to saying: 
   * for (unsigned int i = 0 ; i < length ; i++) {
   *     y[i] = x1[i] + x2[i];
   * }
   * but the operation is carried out on the vectors that are imported from the 
   * arbb library.
   **/
  y = x1 + x2;
}

void outputresult(const float* arr) {
  for (unsigned int i = 0; i < length; i++) {
    std::cout << arr[i] << " ";
  }
  std::cout << std::endl;
}
